namespace KillerKitty {

	//Used to create functors that are used to sort dynamic signatures, based on prefixes.
	class recordCompare  : public std::binary_function<const kkSignature_t * ,const kkSignature_t * ,bool>{
	public:
		bool operator () (const kkSignature_t * a, const kkSignature_t * b) const {

			//cout<<"\nComparing : "<<a->virNamePrefix<<","<<b->virNamePrefix;
			//cin.get();
			return ((a->virNamePrefix - b->virNamePrefix) <0);	

		}


	};


	//Abstract Signature parser class
	//provides interfaces to parse and read the signatures
	class kkSignatureParser : public KillerKittyBase {
	protected:
		long sigCount;

	public:
		kkSignatureParser(char *path="kkDataBase\\");
		virtual ~kkSignatureParser();

		//get the total sigs available
		long virtual getSigCount()=0;	
		//start parsing the signatures, returns number of signatures successfully parsed
		long virtual parseSignatures()=0;	
		//returns th signature at given index
		virtual kkSignature_t& getSignatureInfo(long index)=0;
		//operator [] has been overloaded to return the virus record at a particular index
		virtual kkSignature_t& operator[](long index)=0;
		//Returns a pointer to the record structure at the required index in the database of static viruses
		virtual kkSignature_t* getStaticSignatureInfo(long index)=0;
		//Returns a pointer to the record structure at the required index in the database of dynamic viruses
		virtual kkSignature_t* getDynamicSignatureInfo(long index)=0;
		//Returns the range of static or dynamic signatures, as specified by sigType
		virtual pair< unsigned long, unsigned long > getSigRange(sigType_e sigType)=0;

		static void printDetails(kkSignature_t& sig);

	};
	
	kkSignatureParser::kkSignatureParser(char *path) {
		sigCount=0;
	}

	kkSignatureParser::~kkSignatureParser() {
	}

	//prints the details of the given signature
	void kkSignatureParser::printDetails(kkSignature_t& sig) {
		string virType_s[] = {"virAny","virPE","virOLE2","virHTML","virMail","virGraphics","virELF"};
		string sigType_s[] = {"sigStatic","sigDynamic","sigOther"};
		string srcFile_s[]= {"daily.hdb","main.hdb","daily.ndb","main.ndb","daily.db","main.db"};
        string virNamePrefixes_s[] = { "ASP", "Adware", "BAT", "DOS", "Dialer", "DoS", "Email", "Exploit", 
						   "Flodder", "Flooder", "HTML", "IRC", "JS", "Joke", "O2000M", "O97M", 
						   "PHP", "Trojan","VBS", "VirTool", "Virtool", "W2000M", "W97M", "Worm", 
						   "X2000M", "X97M", "noPrefix" };
		
		
		cout<<"\nName : "<<sig.name<<"\nHexCode: "<<sig.hexcode
			 <<"\nOffset : "<<sig.offset<<"\nFile Length : "
			 <<sig.fileLength<<"\nSource File : "<<srcFile_s[sig.srcFile]
		     <<"\nSig Type : "<<sigType_s[sig.sigType]<<"\nVir Type : "
			 <<virType_s[sig.virType]<<"\nVir Name Prefix : "<<virNamePrefixes_s[sig.virNamePrefix]<<endl;
		
	}


	//Clam Signature parser
	//class to parse the unpacked clam antivirus signatures

	class kkClamSignatureParser : public kkSignatureParser {
    
	private:
		//Used to store the database
		//TODO:Replace with a hashtable? compare perofrmance first.
		vector< kkSignature_t * > dbHolder;
		//Used  to store the path of the DB Files from which the virus records are loaded
		char *path;
		unsigned long virRecIndices[6];
		void assignData(kkSignature_t *currRecord,char *data,recFields_e fieldIndex);
		string getDelimiter(string fName);
		recFields_e getNextFieldIndex(string fName,recFields_e currIndex);
		sigType_e getDatabaseType(string fName);
		virNamePrefix_e getVirNamePrefix(const char *virName);
		string getExtension(string fName);
		kkSignature_t * cloneRecord(kkSignature_t *source);
		bool compare(kkSignature_t *&a, kkSignature_t *&b);
		void setHolderCapacity(void);
		void writeNewCount(void);
		
		
				
	public:
		//"kkDataBase\\" is the default path if no explicit path was specified
		kkClamSignatureParser(char *path="kkDataBase\\");
		virtual ~kkClamSignatureParser();
		long getSigCount();
		long parseSignatures();
		kkSignature_t& getSignatureInfo(long index);
		kkSignature_t& operator[](long index);
		kkSignature_t * getStaticSignatureInfo(long index);
		kkSignature_t * getDynamicSignatureInfo(long index);
		vector< prefixInfo > prefixes;
		pair< unsigned long, unsigned long > getSigRange(sigType_e sigType);

		
	};

	kkClamSignatureParser::kkClamSignatureParser(char *path) {
        this->path=path;
		log.write(this,"Signature parser init",debug);		
	}

	kkClamSignatureParser::~kkClamSignatureParser() {
		long i=0;
        //freeing up the pointers contained in the vector
		for(i=0;i<sigCount;i++){

			delete dbHolder[i];
		}
	}

	long kkClamSignatureParser::getSigCount() {

		return sigCount;
	}
	
	long kkClamSignatureParser::parseSignatures() {
        
		//The file names are static (for now), and they are stored as an array of string objects.
		//The list of files must be in the same order as in the definition of the enum srcFiles_e in "stdafx.h"

		string fNames[]={"daily.hdb","main.hdb","daily.ndb","main.ndb","daily.db","main.db"};
		string sPath=path,fullPath,s;

		char *result,*str;
		int i=0,j=0;
		int vIndex=0;
		kkSignature_t *temp;

		//dbReader is used to open the database for reading 
		ifstream *dbReader;

		//record represents any record in the database, and is a structure of type kkSignature_t
		kkSignature_t *record;

		recFields_e currField;

		//Checking if the file "SigCounter.kk" exists. 
		//If it does exist then the capacity of the vector is set to the value specified in it.
	
		setHolderCapacity();
		     
		for(i=0;i<6;i++){

		//Appending file name to path of the folder that contains the database files
		 fullPath=sPath+fNames[i];
		 //Allocating memory to dbReader pointer
		 dbReader=new ifstream;
		 //Opening a file for reading
		 dbReader->open(fullPath.c_str());		 

		 if(!dbReader->is_open()){
			 //Write to log here in case the file cant b opened
			 log.write(this,"Error opening database "+fullPath,error);
			 return -1;
		 }

          /*
		  The first two files are the databases of static viruses. (*.hdb files)
		  A record in their databases is of the following type:

		  MD5Signature:FileLength:MalwareName


		  The next four files in fName[] array are the databases of dynamic viruses. (*.ndb,*.db files)
		  A record in their databases is any one of the following two types :

		  Type 1: *.db files  

			      MalwareName=HexSignature

		  Type 2: *.ndb files
				
				  MalwareName:TargetType:Offset:HexSignature[:MinEngineFunctionalityLevel]

				where TargetType is a decimal number describing a target type:
				 0 = any file 
				 1 = Portable Executable
				 2 = OLE2 component (e.g. VBA script)
				 3 = HTML (normalised)
				 4 = Mail file
				 5 = Graphics (to help catching exploits in JPEG files)

				 Each of the target types are denoted by the enum constants specified in virType_e in "stdafx.h" header file


		  */
		 //Checking if dbReader has reached end of file, before copying further records from the database
			
		 virRecIndices[vIndex]=dbHolder.size();
		 vIndex++;
         
		 while(!dbReader->eof()) {

			 //Gets a line from dbReader and stores it in the string obj s.
			
			 getline(*dbReader,s,'\n');

			 if(! stricmp(s.c_str(),""))
			 { 
			  /*
			   Use this if an empty record indicating a file separator is to be introduced.
			   record = new kkSignature_t();
			   record->name="NULL";
			   record->offset="NULL";
			   record->hexcode="NULL";
			   record->srcFile=noFile;
			   record->sigType=noSig;
			   record->virType=noVir;
			   record->fileLength=0;
			   dbHolder.push_back(record);
		     */
			   continue;
			 }

			 /*
			  since strtok() function needs a c-style string, the currently copied string object s,
			  is converted into a c-style string str
			 */
			 
			 str=new char[s.size()+1];
			 strcpy(str,s.c_str());
			 		 
			 //Creating a new record
	 		 record = new kkSignature_t(getDatabaseType(fNames[i]),(srcFile_e)i);

    		 //currField is used to index the fields in the record structure
			 currField=getNextFieldIndex(fNames[i],noField);

			 //Tokenises the current record read, and assigns the data to the fields in the variable record
			 for(result=strtok(str,getDelimiter(fNames[i]).c_str());result!=NULL;result=strtok(NULL,getDelimiter(fNames[i]).c_str())){
				 assignData(record,result,currField);
     			 currField=getNextFieldIndex(fNames[i],currField);
			 }
			 dbHolder.push_back(record);
			 //Sorting static dbase records in ascending order of fileLength field.
			 //TODO: replace string compare with a flag?
			 if((dbHolder.size()>=2) && (strcmp(getExtension(fNames[i]).c_str(),"hdb")==0))	 {

				 //Insertion sort
				 //--------------------------------------------
				 temp=cloneRecord(dbHolder[dbHolder.size()-1]);
				 for(j=dbHolder.size()-2;j>=0 && temp->fileLength <= dbHolder[j]->fileLength;j--)  
					 dbHolder[j+1]=dbHolder[j];
				 dbHolder[j+1]=temp;
				 //--------------------------------------------
			 }	
			 
		 }
		 //Deleting temporary pointer str and closing dbReader, so that it may open a new database in the next iteration
		 delete str;
		 dbReader->close();
		 
	  }
		sigCount=dbHolder.size();

		//Test Lines for checking if sorting of static dbase files was correct
		/*
		FILE *fReader;
		fReader=fopen("c:\\test.txt","wb");
		char fLengthTemp[30];

		for(i=virRecIndices[daily_hdb];i<virRecIndices[daily_ndb];i++){

			fputs(dbHolder[i]->name,fReader);
			fputs(" : ",fReader);
			fputs(itoa(dbHolder[i]->fileLength,fLengthTemp,10),fReader);
			fputs("\n",fReader);		 

		}

		fclose(fReader);
		*/

		//Writing count of records to "sigCounter.kk" file.
		writeNewCount();
		
		vector<kkSignature_t *>::iterator it=dbHolder.begin();
		while(*it != dbHolder[virRecIndices[daily_ndb]]) {it++;}

		//----sorting dynamic signatures by prefixes----------
		sort(it,dbHolder.end(),recordCompare());
		//----------------------------------------------------

		log.write(this,"Virus database uploaded to memory successfully",debug);
		//cin.get();
		
		
		//Saves the prefixes of the viruses in the array : prefixes[], so that it can be used for filtering the possible viruses during detection
		virNamePrefix_e last=dbHolder[virRecIndices[daily_ndb]]->virNamePrefix;		
		int currStart;		
		for(i=currStart=virRecIndices[daily_ndb];i<dbHolder.size();i++){

			if(last != dbHolder[i]->virNamePrefix){								
				prefixes.push_back(prefixInfo(currStart,i-1,last));
				last=dbHolder[i]->virNamePrefix;
				currStart=i;			

			}
		}

		//Pushing noPrefix record
		prefixes.push_back(prefixInfo(currStart,i-1,last));

		return sigCount;
	}
	//Copies the string data to currRecord, at the field specified by fieldIndex
	void kkClamSignatureParser::assignData(kkSignature_t *currRecord,char *data,recFields_e fieldIndex){

		char *dataNew=new char[strlen(data)+1];
		strcpy(dataNew,data);

		switch(fieldIndex){
			case name:
     			currRecord->name=dataNew;
				currRecord->virNamePrefix=getVirNamePrefix(dataNew);
				break;
     		case hexcode:
				currRecord->hexcode=dataNew;
				break;
     		case offset:
				currRecord->offset=dataNew;
	  			break;
     		case fileLength:
				currRecord->fileLength=atoi(dataNew);
       	 		break;
			case virType:
				currRecord->virType=(virType_e)atoi(dataNew);
		}
	}


	
	kkSignature_t& kkClamSignatureParser::getSignatureInfo(long index) {
        
		//Raise error if index is out of range
  		assert( (unsigned long)index<dbHolder.size() && index>=0 );
		
		return *(dbHolder[index]);	    
		
	}

	kkSignature_t& kkClamSignatureParser::operator[](long index){

		return getSignatureInfo(index);    
	}

	//returns the delimiter in the record for a particular virus type

	string kkClamSignatureParser::getDelimiter(string fName){
	
		if(fName.find("hdb",0) != string::npos )
			return ":";
		else if((fName.find("ndb",0) != string::npos ))
			return ":";
		return "=";
	}


	//This is used to iterate throught the fields of different virus records
	recFields_e kkClamSignatureParser::getNextFieldIndex(string fName,recFields_e currField){

		if(fName.find("hdb",0) != string::npos){

			switch(currField){

				case noField:
					return hexcode;
				case hexcode:
					return fileLength;
				case fileLength:
					return name;
				default:
					return noField;
			}
		}
		else if( fName.find("ndb",0) != string::npos ){
			switch(currField){

				case noField:
					return name;
				case name:
					return virType;
				case virType:
					return offset;
				case offset:
					return hexcode;
				default:
					return noField;
			}

		}
		else{
			switch(currField){
        
			case noField:
				return name;
			case name:
				return hexcode;
			default:
				return noField;
			}
		}
	}
    //Returns an enum constant for the type of database, by checking the extension of the database file
	sigType_e kkClamSignatureParser::getDatabaseType(string fName){

		if(fName.find("hdb") != string::npos){
			return sigStatic;
		}
		else if(fName.find("ndb") != string::npos ){
            return sigDynamic;
		}
		//For .db files
		return sigDynamic;
	}

	string kkClamSignatureParser::getExtension(string fName){
		if(fName.find("hdb",0) != string::npos){
			return "hdb";
		}
		else if(fName.find("ndb",0) != string::npos){
			return "ndb";
		}
		return "db";
	}

	//---------------------------------------------------------------------
	//The following method depends on the fields present in kkSignature_t
	//---------------------------------------------------------------------

	kkSignature_t * kkClamSignatureParser::cloneRecord(kkSignature_t *source) {

		kkSignature_t *clone=new kkSignature_t();

		clone->name=new char[strlen(source->name)+1];
		strcpy(clone->name,source->name);
		clone->hexcode=new char[strlen(source->hexcode)+1];
		strcpy(clone->hexcode,source->hexcode);
		clone->offset=new char[strlen(source->offset)+1];
		strcpy(clone->offset,source->offset);

		clone->sigType=source->sigType;
		clone->virType=source->virType;
		clone->srcFile=source->srcFile;
		clone->virNamePrefix=source->virNamePrefix;
		clone->fileLength=source->fileLength;

		return  clone;

	}

	kkSignature_t* kkClamSignatureParser::getStaticSignatureInfo(long index){
		//index shouldnt exceed the range of static virus records
		assert( index >= 0 && (unsigned long)index < virRecIndices[daily_ndb]);

		return dbHolder[index];

	}
	kkSignature_t* kkClamSignatureParser::getDynamicSignatureInfo(long index){
		//index shouldnt exceed the range for dynamic virus records
		assert( (unsigned long)index < dbHolder.size() && index >= 0 );

		return dbHolder[index+virRecIndices[daily_ndb]];

	}

	//Checking if the file "SigCounter.kk" exists. 
	//If it does then the size of the vector is set to the capacity specified in it.
	void kkClamSignatureParser::setHolderCapacity(void){
		string pathString(path);
		string sigCounterPath ( pathString+"SigCounter.kk" );
		string val;
		char *endPtr;
		ifstream sigCounterReader(sigCounterPath.c_str());
		if( sigCounterReader.is_open() ){
			getline(sigCounterReader,val,'\n');
			unsigned long ul=strtoul(val.c_str(),&endPtr,10);
			//cout<<"\n\nValue read : "<<ul<<endl<<endl;			
			dbHolder.reserve(ul);						
		}

	}
	void kkClamSignatureParser::writeNewCount(void){

		char sigCountBuf[30];
		string pathString(path);
		string sigCounterPath(pathString+"SigCounter.kk");
		FILE *fp=fopen(sigCounterPath.c_str(),"w+");
		fputs(itoa(sigCount,sigCountBuf,10),fp);
		fclose(fp);
		cout<<"\nwrote to sigCounter.kk\n";

	}
	
	virNamePrefix_e kkClamSignatureParser::getVirNamePrefix(const char *virName){

		static string virNamePrefixes_s[] = { "ASP", "Adware", "BAT", "DOS", "Dialer", "DoS", "Email", "Exploit", 
							 "Flodder", "Flooder", "HTML", "IRC", "JS", "Joke", "O2000M", "O97M", 
							"PHP", "Trojan","VBS", "VirTool", "Virtool", "W2000M", "W97M", "Worm", 
							"X2000M", "X97M", "noPrefix" };
		int i=0;
		
		string virNameStr(virName);
		for(i=0;i<PREFIXCNT;i++){
			if(virNameStr.find(virNamePrefixes_s[i],0)!= string::npos)
				return (virNamePrefix_e)i;
		}   
		
		return (virNamePrefix_e)i;
	}
	
	pair< unsigned long, unsigned long > kkClamSignatureParser::getSigRange(sigType_e sigType){

		assert(sigType==sigStatic || sigType==sigDynamic);

		if(sigType == sigStatic)
			return pair< unsigned long, unsigned long >(0,virRecIndices[daily_ndb]-1);
		else if(sigType==sigDynamic)
			return pair< unsigned long, unsigned long >(virRecIndices[daily_ndb],dbHolder.size()-1);


	}
	
};

	





